package org.dromara.wechat.service;

import cn.binarywang.wx.miniapp.api.WxMaCodeService;
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeAuditStatus;
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest;
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeSubmitAuditRequest;
import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionInfo;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.therapi.runtimejavadoc.repack.com.eclipsesource.json.JsonObject;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.open.api.WxOpenConfigStorage;
import me.chanjar.weixin.open.api.impl.WxOpenInRedissonConfigStorage;
import me.chanjar.weixin.open.api.impl.WxOpenMessageRouter;
import me.chanjar.weixin.open.api.impl.WxOpenServiceImpl;
import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken;
import me.chanjar.weixin.open.bean.result.WxFastMaSetNickameResult;
import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerOptionResult;
import me.chanjar.weixin.open.bean.result.WxOpenResult;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.redis.config.RedisConfig;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.wechat.constants.WechatConstants;
import org.dromara.wechat.domain.CategoryBo;
import org.dromara.wechat.domain.MiniProgramDomainBo;
import org.dromara.wechat.domain.config.WechatOpenProperties;
import org.dromara.wechat.domain.vo.ConfiguredCategoriesVo;
import org.dromara.wechat.domain.vo.WechatCategoryVo;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.locks.Lock;

import static me.chanjar.weixin.open.api.WxOpenComponentService.API_COMPONENT_TOKEN_URL;
import static me.chanjar.weixin.open.api.WxOpenComponentService.FAST_REGISTER_WEAPP_URL;
import static me.chanjar.weixin.open.api.WxOpenMaBasicService.OPEN_MODIFY_SIGNATURE;
import static me.chanjar.weixin.open.api.WxOpenMaBasicService.OPEN_SET_NICKNAME;
import static org.dromara.common.core.constant.Constants.SUCCESS;
import static org.dromara.wechat.constants.WechatConstants.ERRCODE;
import static org.dromara.wechat.constants.WechatConstants.ERRMSG;


@Service
@EnableConfigurationProperties({WechatOpenProperties.class})
@Slf4j
public class WechatOpenService extends WxOpenServiceImpl {

    @Autowired
    private WechatOpenProperties wechatOpenProperties;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisConfig redisConfig;
    private WxOpenMessageRouter wxOpenMessageRouter;
    private WxMaCodeService wxMaCodeService;


    @PostConstruct
    public void init() throws WxErrorException {
        WxOpenInRedissonConfigStorage inRedissonConfigStorage = new WxOpenInRedissonConfigStorage(redissonClient);
        inRedissonConfigStorage.setComponentAppId(wechatOpenProperties.getComponentAppId());
        inRedissonConfigStorage.setComponentAppSecret(wechatOpenProperties.getComponentSecret());
        inRedissonConfigStorage.setComponentToken(wechatOpenProperties.getComponentToken());
        inRedissonConfigStorage.setComponentAesKey(wechatOpenProperties.getComponentAesKey());
        setWxOpenConfigStorage(inRedissonConfigStorage);
        wxOpenMessageRouter = new WxOpenMessageRouter(this);
        wxOpenMessageRouter.rule().handler((wxMpXmlMessage, map, wxMpService, wxSessionManager) -> {
            log.info("\n接收到 {} 公众号请求消息，内容：{}", wxMpService.getWxMpConfigStorage().getAppId(), wxMpXmlMessage);
            return null;
        }).next();
    }


    public WxOpenMessageRouter getWxOpenMessageRouter() {
        return wxOpenMessageRouter;
    }


    /**
     * 启动票据推送服务
     *
     * @throws WxErrorException
     */
    public void startPushTicket() throws WxErrorException {
        log.info("start push ticket");
        JsonObject jsonObject = new JsonObject();
        jsonObject.add("component_appid", wechatOpenProperties.getComponentAppId());
        jsonObject.add("component_secret", wechatOpenProperties.getComponentSecret());
        this.getWxOpenComponentService().startPushTicket();
    }

    /**
     * todo
     * 快速注册企业小程序
     */
    public String registerMiniprogram(String name, String code, String codeType, String legalPersonaWechat, String legalPersonaName, String componentPhone) {
        String redisKey = WechatConstants.MINIPROGRAM_PREFIX + code;
        Object obj = RedisUtils.getCacheObject(redisKey);
        if (obj != null) {
            throw new RuntimeException("检测到频繁提交，请稍后再试");
        }
        RedisUtils.setCacheObject(redisKey, code);
        WxOpenResult wxOpenResult = null;
        try {
            log.info("getComponentVerifyTicket:{}", this.getWxOpenConfigStorage().getComponentVerifyTicket());
            log.info("getToken:{}", this.getWxOpenConfigStorage().getComponentAccessToken());
            wxOpenResult = this.getWxOpenComponentService().fastRegisterWeapp(name, code, codeType.toString(), legalPersonaWechat, legalPersonaName, componentPhone);
//            fastRegisterWeapp(name, code, codeType.toString(), legalPersonaWechat, legalPersonaName, phone);
            log.info("register miniprogram result:{}", wxOpenResult);
        } catch (WxErrorException e) {
            log.error("register miniprogram error", e);
            RedisUtils.deleteObject(redisKey);
            return e.getError().getErrorMsg();
        }
        RedisUtils.deleteObject(redisKey);
        if (StringUtils.isNoneBlank(wxOpenResult.getErrcode()) && "0".equals(wxOpenResult.getErrcode())) {
            return SUCCESS;
        } else {
            return wxOpenResult.getErrmsg();
        }
    }


    public String fastRegisterWeapp(String name, String code, String codeType, String legalPersonaWechat, String legalPersonaName, String componentPhone) throws WxErrorException {
        String response = null;
        try {
            JsonObject jsonObject = new JsonObject();
            jsonObject.add("name", name);
            jsonObject.add("code", code);
            jsonObject.add("code_type", codeType);
            jsonObject.add("legal_persona_wechat", legalPersonaWechat);
            jsonObject.add("legal_persona_name", legalPersonaName);
            jsonObject.add("component_phone", componentPhone);
            response = post(FAST_REGISTER_WEAPP_URL, jsonObject.toString(), "component_access_token");
        } catch (Exception e) {
            log.error("fastRegisterWeapp error", e.getMessage());
            throw new WxErrorException(e);
        }

        return response;
    }

    public String post(String uri, String postData, String accessTokenKey) throws WxErrorException {
        try {
            String componentAccessToken = getComponentAccessToken(false);
            String uriWithComponentAccessToken = uri + (uri.contains("?") ? "&" : "?") + accessTokenKey + "=" + componentAccessToken;
            try {
                return this.post(uriWithComponentAccessToken, postData);
            } catch (WxErrorException e) {
                WxError error = e.getError();
                if (WxConsts.ACCESS_TOKEN_ERROR_CODES.contains(error.getErrorCode())) {
                    // 强制设置access token过期，这样在下一次请求里就会刷新access token
                    Lock lock = this.getWxOpenConfigStorage().getComponentAccessTokenLock();
                    lock.lock();
                    try {
                        if (org.apache.commons.lang3.StringUtils.equals(componentAccessToken, this.getWxOpenConfigStorage().getComponentAccessToken())) {
                            this.getWxOpenConfigStorage().expireComponentAccessToken();
                        }
                    } catch (Exception ex) {
                        this.getWxOpenConfigStorage().expireComponentAccessToken();
                    } finally {
                        lock.unlock();
                    }

                    if (this.getWxOpenConfigStorage().autoRefreshToken()) {
                        log.warn("即将重新获取新的access_token，错误代码：{}，错误信息：{}", error.getErrorCode(), error.getErrorMsg());
                        return this.post(uri, postData, accessTokenKey);
                    }
                }
                if (error.getErrorCode() != 0) {
                    throw new WxErrorException(error, e);
                }
                return null;
            }
        } catch (Exception e) {
            log.error("post error", e.getMessage());
            throw new WxErrorException(e);
        }
    }

    public String getComponentAccessToken(boolean forceRefresh) throws WxErrorException {
        try {
            final WxOpenConfigStorage config = this.getWxOpenConfigStorage();
            if (!config.isComponentAccessTokenExpired() && !forceRefresh) {
                return config.getComponentAccessToken();
            }
            Lock lock = config.getComponentAccessTokenLock();
            lock.lock();
            try {
                if (!config.isComponentAccessTokenExpired() && !forceRefresh) {
                    return config.getComponentAccessToken();
                }
                JsonObject jsonObject = new JsonObject();
                jsonObject.add("component_appid", getWxOpenConfigStorage().getComponentAppId());
                jsonObject.add("component_appsecret", getWxOpenConfigStorage().getComponentAppSecret());
                jsonObject.add("component_verify_ticket", getWxOpenConfigStorage().getComponentVerifyTicket());

                String responseContent = this.post(API_COMPONENT_TOKEN_URL, jsonObject.toString());
                WxOpenComponentAccessToken componentAccessToken = WxOpenComponentAccessToken.fromJson(responseContent);
                config.updateComponentAccessToken(componentAccessToken);
                return config.getComponentAccessToken();
            } finally {
                lock.unlock();
            }
        } catch (Exception e) {
            log.error("getComponentAccessToken error", e.getMessage());
            throw new WxErrorException(e);
        }
    }

    /**
     * todo
     * 获取授权方选项信息
     *
     * @param authorizerAppid 授权方appid
     * @return WxOpenAuthorizerOptionResult
     */
    public WxOpenAuthorizerOptionResult getAuthorizerOptionInfo(String authorizerAppid, String optionName) throws WxErrorException {
        return getWxOpenComponentService().getAuthorizerOption(authorizerAppid, optionName);
    }
//
//    /**
//     * todo
//     * 小程序名称检测
//     *
//     * @param nickName 小程序名称
//     * @return WxFastMaCheckNickameResult
//     */
//    public WxFastMaCheckNickameResult checkNickName(String nickName, String userId) {
//        String redisValue = nickName + "_" + userId;
//        String redisKey = WechatConstants.NICKNAME_CHECK_PREFIX + redisValue;
//        Object obj = RedisUtils.getCacheObject(redisKey);
//        if (obj != null) {
//            throw new RuntimeException("检测到频繁提交，请稍后再试");
//        }
//        RedisUtils.setCacheObject(redisKey, redisValue);
//        log.info("check nickName:{}", nickName);
//        WxFastMaCheckNickameResult result = null;
//        try {
//            result = this.getwxOpenMaService().getBasicService().checkWxVerifyNickname(nickName);
//            log.info("checkNickName result:{}", result);
//        } catch (WxErrorException e) {
//            log.error("checkNickName error", e);
//            e.printStackTrace();
//        }
//        RedisUtils.deleteObject(redisKey);
//        return result;
//    }
//

    /**
     * todo
     * 设置小程序名称
     *
     * @param name 小程序名称
     * @return boolean
     */
    public WxFastMaSetNickameResult setNickName(String authorizerAppid, String name) {
        log.info("set nickName:{}", name);
        WxFastMaSetNickameResult result = new WxFastMaSetNickameResult();
        try {
            String response = setNickname(authorizerAppid, name, null, null, null, null);
            JSONObject jsonObject = JSONObject.parseObject(response);
            result.setErrcode(jsonObject.getString("errcode"));
            result.setErrmsg(jsonObject.getString("errmsg"));
            log.info("setNickName result:{}", response);
        } catch (WxErrorException e) {
            log.error("setNickName error", e);
            throw new ServiceException(e.getError().getErrorMsg());
        }
        return result;
    }

    public String setNickname(String authorizerAppid, String nickname, String idCard, String license, String namingOtherStuff1, String namingOtherStuff2) throws WxErrorException {
        JsonObject params = new JsonObject();
        params.add("nick_name", nickname);
        params.add("id_card", idCard);
        params.add("license", license);
        params.add("naming_other_stuff_1", namingOtherStuff1);
        params.add("naming_other_stuff_2", namingOtherStuff2);
        String url = OPEN_SET_NICKNAME + "?access_token=" + this.getWxOpenComponentService().getAuthorizerAccessToken(authorizerAppid, false);
        return post(url, params.toString());
    }
//
//    /**
//     * todo
//     * 新增临时素材
//     *
//     * @param mediaType 媒体文件类型，分别有图片（image）、语音（voice）、视频（video）和缩略图（thumb）
//     * @param mediaFile 媒体文件
//     * @return 媒体文件id
//     */
//    public String addTemporaryMaterial(String mediaType, File mediaFile, String userId) {
//        String redisValue = mediaType + "_" + mediaFile.getName() + "_" + userId;
//        String redisKey = WechatConstants.ADD_TEMPORARY_MATERIAL_PREFIX + redisValue;
//        Object obj = RedisUtils.getCacheObject(redisKey);
//        if (obj != null) {
//            throw new RuntimeException("检测到频繁提交，请稍后再试");
//        }
//        RedisUtils.setCacheObject(redisKey, redisValue);
//        log.info("addTemporaryMaterial mediaType:{}", mediaType);
//        String mediaId = null;
//        try {
//            mediaId = wxOpenMaService.getMediaService().uploadMedia(mediaType, mediaFile).getMediaId();
//        } catch (WxErrorException e) {
//            log.error("addTemporaryMaterial error", e);
//            e.printStackTrace();
//        }
//        RedisUtils.deleteObject(redisKey);
//        return mediaId;
//    }
//
//    /**
//     * todo
//     * 设置搜索状态
//     *
//     * @param status 状态
//     */
//    public WxOpenResult setSearchStatus(Integer status, String userId) {
//        String redisValue = status + "_" + userId;
//        String redisKey = WechatConstants.SET_SEARCH_STATUS_PREFIX + redisValue;
//        Object obj = RedisUtils.getCacheObject(redisKey);
//        if (obj != null) {
//            throw new RuntimeException("检测到频繁提交，请稍后再试");
//        }
//        RedisUtils.setCacheObject(redisKey, redisValue);
//        log.info("setSearchStatus status:{}", status);
//        WxOpenResult result = null;
//        try {
//            result = wxOpenMaService.changeWxaSearchStatus(status);
//        } catch (WxErrorException e) {
//            log.error("setSearchStatus error", e);
//            e.printStackTrace();
//        }
//        RedisUtils.deleteObject(redisKey);
//        return result;
//    }
//
//    /**
//     * todo
//     * 修改头像
//     *
//     * @param headImgMediaId 头像素材id
//     * @param x1             裁剪框左上角横坐标
//     * @param y1             裁剪框左上角纵坐标
//     * @param x2             裁剪框右下角横坐标
//     * @param y2             裁剪框右下角纵坐标
//     * @return WxOpenResult
//     */
//    public WxOpenResult setHeadImage(String headImgMediaId, float x1, float y1, float x2, float y2, String userId) {
//        String redisValue = headImgMediaId + "_" + userId;
//        String redisKey = WechatConstants.SET_HEAD_IMAGE_PREFIX + redisValue;
//        Object obj = RedisUtils.getCacheObject(redisKey);
//        if (obj != null) {
//            throw new RuntimeException("检测到频繁提交，请稍后再试");
//        }
//        RedisUtils.setCacheObject(redisKey, redisValue);
//        log.info("setHeadImage headImgMediaId:{}", headImgMediaId);
//        WxOpenResult result = null;
//        try {
//            result = wxOpenMaService.getBasicService().modifyHeadImage(headImgMediaId, x1, y1, x2, y2);
//        } catch (WxErrorException e) {
//            log.error("setHeadImage error", e);
//            e.printStackTrace();
//        }
//        RedisUtils.deleteObject(redisKey);
//        return result;
//    }
//
//    /**
//     * todo
//     * 设置小程序介绍
//     *
//     * @param introduction 小程序介绍
//     * @return WxOpenResult
//     */
//    public WxOpenResult setSignature(String introduction, String userId) {
//        String redisValue = introduction + "_" + userId;
//        String redisKey = WechatConstants.SET_INTRODUCTION_PREFIX + redisValue;
//        Object obj = RedisUtils.getCacheObject(redisKey);
//        if (obj != null) {
//            throw new RuntimeException("检测到频繁提交，请稍后再试");
//        }
//        RedisUtils.setCacheObject(redisKey, redisValue);
//        log.info("setIntroduction introduction:{}", introduction);
//        WxOpenResult result = null;
//        try {
//            result = wxOpenMaService.getBasicService().modifySignature(introduction);
//        } catch (WxErrorException e) {
//            log.error("setIntroduction error", e);
//            e.printStackTrace();
//        }
//        RedisUtils.deleteObject(redisKey);
//        return result;
//    }
//
//    /**
//     * todo
//     * 获取不同类型主体可设置的类目
//     *
//     * @param type 主体类型，1：公众号，2：小程序
//     * @return 类目列表
//     */
//    public String getAllCategoriesByType(Integer type) throws WxErrorException {
//        JsonObject params = new JsonObject();
//        params.add("verify_type", type);
//        return wxOpenMaService.get(WechatConstants.OPEN_GET_ALL_CATEGORIES_BY_TYPE, params.toString());
//    }
//
//    /**
//     * todo
//     * 添加类目
//     *
//     * @param category 类目
//     * @return WxOpenResult
//     */
//    public WxOpenResult addCategory(WxFastMaCategory category) throws WxErrorException {
//        List<WxFastMaCategory> categoryList = new ArrayList<>();
//        // 校验
//        if (null == category || CollectionUtils.isEmpty(category.getCerticates())) {
//            throw new WxErrorException("类目不能为空");
//        }
//        // 校验资质信息列表
//        for (WxFastMaCategory.Certificate certificate : category.getCerticates()) {
//            if (StringUtils.isEmpty(certificate.getKey()) || StringUtils.isEmpty(certificate.getValue())) {
//                throw new WxErrorException("资质信息不能为空");
//            }
//        }
//        categoryList.add(category);
//        return wxOpenMaService.getBasicService().addCategory(categoryList);
//    }
//
//    /**
//     * todo
//     * 删除类目
//     *
//     * @param firstId  一级类目ID
//     * @param secondId 二级类目ID
//     * @return WxOpenResult
//     */
//    public WxOpenResult delCategory(Integer firstId, Integer secondId) throws WxErrorException {
//        return wxOpenMaService.getBasicService().deleteCategory(firstId, secondId);
//    }
//
//    /**
//     * todo
//     * 修改类目资质信息
//     *
//     * @param category 类目
//     * @return WxOpenResult
//     */
//    public WxOpenResult updateCategory(WxFastMaCategory category) throws WxErrorException {
//        // 校验
//        if (null == category || CollectionUtils.isEmpty(category.getCerticates())) {
//            throw new WxErrorException("类目不能为空");
//        }
//        // 校验资质信息列表
//        for (WxFastMaCategory.Certificate certificate : category.getCerticates()) {
//            if (StringUtils.isEmpty(certificate.getKey()) || StringUtils.isEmpty(certificate.getValue())) {
//                throw new WxErrorException("资质信息不能为空");
//            }
//        }
//        return wxOpenMaService.getBasicService().modifyCategory(category);
//    }
//
//    /**
//     * todo
//     * 小程序认证
//     *
//     * @return MaAuthSubmitResult
//     * @throws WxErrorException
//     */
//    public MaAuthSubmitResult miniprogramAuth() throws WxErrorException {
//        MaAuthSubmitParamAuthData authData = new MaAuthSubmitParamAuthData();
//        MaAuthSubmitParam param = new MaAuthSubmitParam();
//        return wxOpenMaService.getAuthService().submit(param);
//    }
//
//    /**
//     * todo
//     * 小程序认证重新提审
//     *
//     * @return MaAuthSubmitResult
//     * @throws WxErrorException
//     */
//    public MaAuthSubmitResult miniprogramAuthReaudit() throws WxErrorException {
//        MaAuthResubmitParam param = new MaAuthResubmitParam();
//        return wxOpenMaService.getAuthService().resubmit(param);
//    }
//
//    /**
//     * todo
//     * 小程序认证上传补充材料
//     *
//     * @return MaAuthUploadResult
//     * @throws WxErrorException
//     */
//    public MaAuthUploadResult uploadAuthMaterial() throws WxErrorException {
//        CommonUploadData commonUploadData = new CommonUploadData();
//        return wxOpenMaService.getAuthService().upload(commonUploadData);
//    }
//
//    /**
//     * todo
//     * 查询人脸核身任务状态
//     */
//    public WxOpenIcpVerifyTaskResult queryIcpVerifyTask(String taskId) throws WxErrorException {
//        return wxOpenMaService.getIcpService().queryIcpVerifyTask(taskId);
//    }
//
//    /**
//     * todo
//     * 发起小程序管理员人脸核身
//     *
//     * @return WxOpenIcpCreateIcpVerifyTaskResult
//     * @throws WxErrorException
//     */
//    public WxOpenIcpCreateIcpVerifyTaskResult miniprogramAuthFaceSearch() throws WxErrorException {
//        return wxOpenMaService.getIcpService().createIcpVerifyTask();
//    }
//
//    /**
//     * todo
//     * 上传小程序备案媒体材料
//     *
//     * @return WxOpenUploadIcpMediaResult
//     * @throws WxErrorException
//     */
//    public WxOpenUploadIcpMediaResult miniprogramAuthMediaUpload() throws WxErrorException {
//        WxOpenUploadIcpMediaParam param = new WxOpenUploadIcpMediaParam();
//        return wxOpenMaService.getIcpService().uploadIcpMedia(param);
//    }
//
//    /**
//     * todo
//     * 申请小程序备案
//     *
//     * @return WxOpenApplyIcpFilingResult
//     * @throws WxErrorException
//     */
//    public WxOpenApplyIcpFilingResult applyIcpFiling() throws WxErrorException {
//        WxOpenApplyIcpFilingParam param = new WxOpenApplyIcpFilingParam();
//        return wxOpenMaService.getIcpService().applyIcpFiling(param);
//    }
//
//    /**
//     * todo
//     * 获取小程序备案状态及驳回原因
//     *
//     * @return WxOpenIcpEntranceInfoResult
//     * @throws WxErrorException
//     */
//    public WxOpenIcpEntranceInfoResult getIcpFiling() throws WxErrorException {
//        return wxOpenMaService.getIcpService().getIcpEntranceInfo();
//    }
//
//    /**
//     * todo
//     * 获取小程序已备案详情
//     *
//     * @return WxOpenOnlineIcpOrderResult
//     * @throws WxErrorException
//     */
//    public WxOpenOnlineIcpOrderResult getOnlineIcpOrder() throws WxErrorException {
//        return wxOpenMaService.getIcpService().getOnlineIcpOrder();
//    }
//
//    /**
//     * todo
//     * 获取小程序备案媒体材料
//     *
//     * @param mediaId 媒体id
//     * @return File
//     * @throws WxErrorException
//     */
//    public File getIcpMaterial(String mediaId) throws WxErrorException {
//        return wxOpenMaService.getIcpService().getIcpMedia(mediaId);
//    }
//
//
//    /**
//     * todo
//     * 配置小程序服务器域名
//     *
//     * @return WxOpenResult
//     * @throws WxErrorException
//     */
//    public WxMaDomainAction setServerDomain() throws WxErrorException {
//        WxMaDomainAction domainAction = new WxMaDomainAction();
//        return wxOpenMaService.getSettingService().modifyDomain(domainAction);
//    }
//
//    /**
//     * todo
//     * 配置小程序业务域名
//     *
//     * @param requestDomain   业务域名
//     * @param wsRequestDomain 业务websocket域名
//     * @return WxOpenResult
//     * @throws WxErrorException
//     */
//    public WxMaDomainAction setBusinessDomain(String requestDomain, String wsRequestDomain) throws WxErrorException {
//        WxMaDomainAction domainAction = new WxMaDomainAction();
//        return wxOpenMaService.getSettingService().setWebViewDomain(domainAction);
//    }


    /**
     * todo
     * 上传代码并生成体验版
     *
     * @throws WxErrorException
     */
    public void codeCommitAndPreview(String templateId, String extJson) throws WxErrorException {
        WxMaCodeCommitRequest commitRequest = new WxMaCodeCommitRequest();
        wxMaCodeService.commit(commitRequest);
    }

    /**
     * todo
     * 提交代码审核
     *
     * @return long 审核单id
     * @throws WxErrorException
     */
    public long codeAudit() throws WxErrorException {
        WxMaCodeSubmitAuditRequest auditRequest = new WxMaCodeSubmitAuditRequest();
        return wxMaCodeService.submitAudit(auditRequest);
    }

    /**
     * todo
     * 查询审核单状态
     *
     * @param auditId 审核单id
     * @return WxMaCodeAuditStatus
     * @throws WxErrorException
     */
    public WxMaCodeAuditStatus queryCodeAudit(long auditId) throws WxErrorException {
        return wxMaCodeService.getAuditStatus(auditId);
    }

    /**
     * todo
     * 撤回代码审核
     *
     * @throws WxErrorException
     */
    public void codeAuditWithdraw() throws WxErrorException {
        wxMaCodeService.undoCodeAudit();
    }

    /**
     * todo
     * 查询小程序版本信息
     *
     * @return WxMaCodeVersionInfo
     * @throws WxErrorException
     */
    public WxMaCodeVersionInfo getCodeVersion() throws WxErrorException {
        return wxMaCodeService.getVersionInfo();
    }

    /**
     * todo
     * 发布已通过审核的小程序代码
     *
     * @throws WxErrorException
     */
    public void codeRelease() throws WxErrorException {
        wxMaCodeService.release();
    }

    /**
     * todo
     * 小程序版本回退
     *
     * @throws WxErrorException
     */
    public void revertCodeRelease() throws WxErrorException {
        wxMaCodeService.revertCodeRelease();
    }

    /**
     * todo
     * 获取体验版二维码
     *
     * @param path 二维码跳转链接
     * @return byte[]
     * @throws WxErrorException
     */
    public byte[] getQrcode(String path) throws WxErrorException {
        return wxMaCodeService.getQrCode(path);
    }

    public String getRidInfo(String rid) {
        String response = null;
        try {
            JsonObject jsonObject = new JsonObject();
            jsonObject.add("rid", rid);
            response = post("https://api.weixin.qq.com/cgi-bin/openapi/rid/get", jsonObject.toString(), "access_token");
            log.info("getRidInfo response:{}", response);
        } catch (Exception e) {
            log.error("fastregisterweapp error", e);
            e.printStackTrace();
        }
        return response;
    }

    public String clearQuota() {
        String response = null;
        try {
            JsonObject jsonObject = new JsonObject();
            jsonObject.add("appid", "wxa981cb8300375011");
            response = post("https://api.weixin.qq.com/cgi-bin/clear_quota", jsonObject.toString(), "access_token");
            log.info("clearQuota response:{}", response);
        } catch (Exception e) {
            log.error("clearQuota error", e);
            e.printStackTrace();
        }
        return response;
    }

    /**
     * todo
     * 设置小程序介绍
     *
     * @param authorizerAppid 授权方appid
     * @param blurb           小程序介绍
     * @return WxOpenResult
     */
    public WxOpenResult setSignature(String authorizerAppid, String blurb) {
        WxOpenResult result = new WxOpenResult();
        try {
            JsonObject params = new JsonObject();
            params.add("signature", blurb);
            String url = OPEN_MODIFY_SIGNATURE + "?access_token=" + this.getWxOpenComponentService().getAuthorizerAccessToken(authorizerAppid, false);
            String response = post(url, params.toString());
            JSONObject jsonObject = JSONObject.parseObject(response);
            result.setErrcode(String.valueOf(jsonObject.getInteger(ERRCODE)));
            result.setErrmsg(jsonObject.getString(ERRMSG));
            log.info("setSignature result:{}", result);
        } catch (WxErrorException e) {
            log.error("setSignature error", e);
            throw new ServiceException(e.getError().getErrorMsg());
        }
        return result;
    }

    public WxOpenResult setMiniprogramDomain(MiniProgramDomainBo bo) {
        WxOpenResult result = new WxOpenResult();
        try {
            Map<String, Object> params = new HashMap<>();
            String url = null;
            String accessToken = this.getWxOpenComponentService().getAuthorizerAccessToken(bo.getAuthorizerAppid(), false);
            if ("1".equals(bo.getDomainType())) {
                url = WechatConstants.MODIFY_DOMAIN_DIRECTLY_URL + "?access_token=" + accessToken;
                params.put("action", "add");
                params.put("requestdomain", Arrays.asList(bo.getRequestDomain().split(",")));
                params.put("wsrequestdomain", new ArrayList<String>());
                params.put("uploaddomain", Arrays.asList(bo.getUploadDomain().split(",")));
                params.put("downloaddomain", Arrays.asList(bo.getDownloadDomain().split(",")));
                params.put("udpdomain", new ArrayList<String>());
                params.put("tcpdomain", new ArrayList<String>());
            } else if ("2".equals(bo.getDomainType())) {
                // 业务域名需要调用 获取业务域名校验文件
                String confirmFileUrl = WechatConstants.GET_WEBVIEW_DOMAIN_CONFIRM_FILE_URL + "?access_token=" + accessToken;
                String confirmFileResponse = post(confirmFileUrl, params.toString());
                JSONObject confirmFileJsonObject = JSONObject.parseObject(confirmFileResponse);
                log.info("confirmFileResponse:{}", confirmFileResponse);
                if (WechatConstants.SUCCESS == confirmFileJsonObject.getInteger(ERRCODE)) {
                    url = WechatConstants.SET_WEBVIEW_DOMAIN_DIRECTLY_URL + "?access_token=" + accessToken;
                    params.put("action", "add");
                    params.put("webviewdomain", bo.getWebViewDomain());
                } else {
                    throw new ServiceException(confirmFileJsonObject.getString(ERRMSG));
                }
            }
            String response = post(url, JSONObject.toJSONString(params));
            JSONObject jsonObject = JSONObject.parseObject(response);
            result.setErrcode(String.valueOf(jsonObject.getInteger(ERRCODE)));
            result.setErrmsg(jsonObject.getString(ERRMSG));
            log.info("setSignature result:{}", result);
        } catch (WxErrorException e) {
            log.error("setMiniprogramDomain error", e);
            throw new ServiceException(e.getError().getErrorMsg());
        }
        return result;
    }

    /**
     * 获取不同类型主体可设置的类目
     *
     * @param authorizerAppid
     * @return
     */
    public WechatCategoryVo getAllCategoriesByType(String authorizerAppid) {
        WechatCategoryVo result = new WechatCategoryVo();
        ObjectMapper mapper = new ObjectMapper();
        try {
            JsonObject params = new JsonObject();
            String accessToken = this.getWxOpenComponentService().getAuthorizerAccessToken(authorizerAppid, false);
            String url = WechatConstants.OPEN_GET_ALL_CATEGORIES_BY_TYPE_URL + "?access_token=" + accessToken;
            // 如果不填，默认传0；个人主体是0；企业主体是1；政府是2；媒体是3；其他组织是4
            params.add("verify_type", "1");
            String response = post(url, params.toString());
            JSONObject jsonObject = JSONObject.parseObject(response);
            log.info("getAllCategoriesByType response:{}", response);
            if (WechatConstants.SUCCESS == jsonObject.getInteger(ERRCODE)) {
                String categoriesList = jsonObject.getString("categories_list");
                result = mapper.readValue(categoriesList, WechatCategoryVo.class);
            } else {
                throw new ServiceException(jsonObject.getString(ERRMSG));
            }
            log.info("getAllCategoriesByType result:{}", result);

        } catch (Exception e) {
            log.error("getAllCategoriesByType error", e);
            throw new ServiceException(e.getMessage());
        }
        return result;
    }

    /**
     * 获取已设置的所有类目
     *
     * @param authorizerAppid
     * @return
     */
    public ConfiguredCategoriesVo getAllCategories(String authorizerAppid) {
        ConfiguredCategoriesVo result = new ConfiguredCategoriesVo();
        try {
            JsonObject params = new JsonObject();
            String accessToken = this.getWxOpenComponentService().getAuthorizerAccessToken(authorizerAppid, false);
            String url = WechatConstants.GET_CATEGORY_URL + "?access_token=" + accessToken;
            String response = post(url, params.toString());
            result = JSONObject.parseObject(response, ConfiguredCategoriesVo.class);
            log.info("getAllCategories result:{}", result);
        } catch (Exception e) {
            log.error("getAllCategories error", e);
            throw new ServiceException(e.getMessage());
        }
        return result;
    }

    /**
     * 新增类目
     * @param authorizerAppid
     * @param bo
     * @return
     */
    public WxOpenResult addCategory(String authorizerAppid, CategoryBo bo) {
        WxOpenResult result = new WxOpenResult();
        try {
            List<Map<String, Object>> categories = new ArrayList<>();
            Map<String, Object> categoryMap = new HashMap<>();
            String accessToken = this.getWxOpenComponentService().getAuthorizerAccessToken(authorizerAppid, false);
            String url = WechatConstants.ADD_CATEGORY_URL + "?access_token=" + accessToken;
            categoryMap.put("first", bo.getFirst());
            categoryMap.put("second", bo.getSecond());
            categories.add(categoryMap);
            // 构建最终参数
            Map<String, Object> params = new HashMap<>();
            params.put("categories", categories);
            String response = post(url, JSONObject.toJSONString(params));
            JSONObject jsonObject = JSONObject.parseObject(response);
            result.setErrcode(String.valueOf(jsonObject.getInteger(ERRCODE)));
            result.setErrmsg(jsonObject.getString(ERRMSG));
            log.info("addCategory result:{}", result);
        } catch (WxErrorException e) {
            log.error("addCategory error", e);
            result.setErrcode(String.valueOf(e.getError().getErrorCode()));
            result.setErrmsg(e.getError().getErrorMsg());
        }
        return result;
    }

    /**
     * 删除已设置的类目
     *
     * @param authorizerAppid
     * @param categoryBo
     * @return
     */
    public WxOpenResult deleteCategory(String authorizerAppid, CategoryBo categoryBo) {
        WxOpenResult result = new WxOpenResult();
        try {
            Map<String, Object> params = new HashMap<>();
            String accessToken = this.getWxOpenComponentService().getAuthorizerAccessToken(authorizerAppid, false);
            String url = WechatConstants.DELETE_CATEGORY_URL + "?access_token=" + accessToken;
            params.put("first", categoryBo.getFirst());
            params.put("second", categoryBo.getSecond());
            String response = post(url, JSONObject.toJSONString(params));
            JSONObject jsonObject = JSONObject.parseObject(response);
            result.setErrcode(String.valueOf(jsonObject.getInteger(ERRCODE)));
            result.setErrmsg(jsonObject.getString(ERRMSG));
            log.info("deleteCategory result:{}", result);
        } catch (WxErrorException e) {
            log.error("deleteCategory error", e);
            result.setErrcode(String.valueOf(e.getError().getErrorCode()));
            result.setErrmsg(e.getError().getErrorMsg());
        }
        return result;
    }
}
